﻿
using System;
using System.IO;

namespace EmperialApps.WeatherSpark.Data.Internal {

    // http://monotorrent.blogspot.com/2009/11/dont-binaryreaderpeekchar-at-me.html
    public class PeekableStream : Stream {

        private readonly Stream _input;
        private readonly byte[] _peekBuffer;
        private bool _hasPeeked;

        public PeekableStream( Stream input ) {
            this._input = input;
            this._peekBuffer = new byte[1];
        }

        public override bool CanRead {
            get { return this._input.CanRead; }
        }

        public override bool CanSeek {
            get { return this._input.CanSeek; }
        }

        public override bool CanWrite {
            get { return false; }
        }

        public override long Length {
            get { return this._input.Length; }
        }

        public override void Flush( ) {
            throw new NotSupportedException( );
        }

        public int PeekByte( ) {
            if( !this._hasPeeked )
                this._hasPeeked = this.Read( this._peekBuffer, 0, 1 ) == 1;

            return this._hasPeeked
                 ? this._peekBuffer[0]
                 : -1;
        }

        public override int ReadByte( ) {
            if( this._hasPeeked ) {
                this._hasPeeked = false;
                return this._peekBuffer[0];
            }

            return base.ReadByte( );
        }

        public override long Position {
            get {
                return this._hasPeeked
                     ? this._input.Position - 1
                     : this._input.Position;
            }

            set {
                if( value != this.Position ) {
                    this._hasPeeked = false;
                    this._input.Position = value;
                }
            }
        }

        public override int Read( byte[] buffer, int offset, int count ) {
            int read = 0;
            if( this._hasPeeked && count > 0 ) {
                this._hasPeeked = false;
                buffer[offset] = this._peekBuffer[0];
                ++offset;
                --count;
                ++read;
            }

            read += this._input.Read( buffer, offset, count );
            return read;
        }

        public override long Seek( long offset, SeekOrigin origin ) {
            long val =
                this._hasPeeked && origin == SeekOrigin.Current
                    ? this._input.Seek( offset - 1, origin )
                    : this._input.Seek( offset, origin );

            this._hasPeeked = false;
            return val;
        }

        public override void SetLength( long value ) {
            throw new NotSupportedException( );
        }

        public override void Write( byte[] buffer, int offset, int count ) {
            throw new NotSupportedException( );
        }

    }

}
